package com.fingard.dsp.bank.directbank.abc23.util;
/**
 * @author Karl
 * @since 2012-10-25
 * Base64要求把每三个8Bit的字节转换为四个6Bit的字节（3*8 = 4*6 = 24），
 * 然后把6Bit再添两位高位0，组成四个8Bit的字节，也就是说，转换后的字符串理论上将要比原来的长1/3。
 * 关于这个编码的规则：
　　①.把3个字符变成4个字符..
　　②每76个字符加一个换行符..
　　③.最后的结束符也要处理.. 
 */
public class Base64Helper {

	/**
	 * 多少个 base64 字符作为一行
	 */
	private static final int LINE_LENGTH = 76;
	private static char PAD = '=';
	/**
	 * 每行最后添加的换行字符
	 */
	private static final char[] LINE_BREAKS = new char[]{'\r', '\n'};
    private static final char[] STANDARD_ENCODE_TABLE = new char[]{
    	'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 
        'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d', 
        'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
        't','u','v','w','x','y','z','0','1','2','3','4','5','6','7', 
        '8','9','+','/' 
    };
    /**
     * url 安全的 Base64 编码，用 -,_替换 +,/
     */
    private static final byte[] URL_SAFE_ENCODE_TABLE = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
    };
    
    /**
     * 
     */
    private static final byte[] DECODE_TABLE = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
        -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 
        54, 55, 56, 57, 58, 59, 60, 61, -1, -1, 
        -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  
         5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
        25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 
        29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 
        39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 
        49, 50, 51
    };
    /**
     * 将字节转换为 base64 编码
     * @param inData
     * @return
     */
    public static String encode(byte[] inData)
    {
    	int caculateCharLength = inData.length * 4 / 3 + inData.length / LINE_LENGTH + 10;
    	char[] outChars = new char[caculateCharLength];
    	int charCount = encode(outChars, inData);
    	return String.valueOf(outChars, 0, charCount);
    }
    /**
     * 将指定字节转换为字符数组输出
     * @param outChars
     * @param inData
     * @return
     */
    public static int encode(char[] outChars, byte[] inData)
    {
    	return encode(outChars, inData, 0, inData.length, false);
    }
    /**
     * 将给定字节数组中的给定长度
     * @param outChars
     * @param inData
     * @param offset
     * @param length
     * @param insertLineBreaks
     * @return
     */
	private static int encode(char[] outChars, byte[] inData, int offset, int length, boolean insertLineBreaks) {
		//每3个 byte 进行一个处理，不足3个的单独处理。
        int lengthmod3 = length%3; 
        //计算到哪个位置的时候说明已经基本处理完毕，只剩下不足3个的 byte 了
        int calcLength = offset + (length - lengthmod3);
        
        int outCharIndex=0;
        //统计编码后的 base64 字符个数，用来判断是否需要加入换行符。
        int charcount = 0;
        //Convert three bytes at a time to base64 notation.  This will consume 4 chars.
        int i;

        // get a pointer to the base64Table to avoid unnecessary range checking
        //fixed(char* base64 = base64Table) 
        { 
            for (i=offset; i<calcLength; i+=3) 
            {
            	//判断是否加入换行符，每 LINE_LENGTH(76) 个字符后面增加一个换行
                if (insertLineBreaks) { 
                    if (charcount == LINE_LENGTH) {
                        outChars[outCharIndex++] = '\r';
                        outChars[outCharIndex++] = '\n';
                        charcount = 0; 
                    }
                    charcount += 4; 
                } 
                //获取第一个字节前 6 个 bit，保存为一个字符。   0xfc=(11111100)             
                outChars[outCharIndex] = STANDARD_ENCODE_TABLE[(inData[i]&0xfc)>>2];
                //获取第一个字节的后 2 个 bit，结合第二个字节的前 4 个 bit 0x03=00000011, 0xf0=11110000
                outChars[outCharIndex+1] = STANDARD_ENCODE_TABLE[((inData[i]&0x03)<<4) | ((inData[i+1]&0xf0)>>4)];
                //获取第二个字节的后 4 个 bit，结合第三个字节的前 2 个 bit 0x0f=00001111, 0xc0=11000000
                outChars[outCharIndex+2] = STANDARD_ENCODE_TABLE[((inData[i+1]&0x0f)<<2) | ((inData[i+2]&0xc0)>>6)];
                //获取第三个字节的后 6 个 bit 0x3f=00111111
                outChars[outCharIndex+3] = STANDARD_ENCODE_TABLE[(inData[i+2]&0x3f)];
                outCharIndex += 4;
            } 

            //Where we left off before 
            i =  calcLength; 
            //如果已经满 76 字符，而且后续还有剩余字节需要处理，则增加换行字符
            if (insertLineBreaks && (lengthmod3 !=0) && (charcount == 76)) { 
                outChars[outCharIndex++] = '\r';
                outChars[outCharIndex++] = '\n';
            }

            switch(lengthmod3)
            { 
            case 2: //One character padding needed 
                outChars[outCharIndex] = STANDARD_ENCODE_TABLE[(inData[i]&0xfc)>>2];
                outChars[outCharIndex+1] = STANDARD_ENCODE_TABLE[((inData[i]&0x03)<<4)|((inData[i+1]&0xf0)>>4)]; 
                outChars[outCharIndex+2] = STANDARD_ENCODE_TABLE[(inData[i+1]&0x0f)<<2];
                outChars[outCharIndex+3] = PAD; //Pad
                outCharIndex+=4;
                break; 
            case 1: // Two character padding needed
                outChars[outCharIndex] = STANDARD_ENCODE_TABLE[(inData[i]&0xfc)>>2]; 
                outChars[outCharIndex+1] = STANDARD_ENCODE_TABLE[(inData[i]&0x03)<<4]; 
                outChars[outCharIndex+2] = PAD; //Pad
                outChars[outCharIndex+3] = PAD; //Pad 
                outCharIndex+=4;
                break;
            }
        } 

        return outCharIndex; 
    }
	
	public static byte[] decode(String base64String)
	{
		return decode(base64String.toCharArray());
	}
	/**
	 * 
	 * @param inChars
	 * @return
	 */
	public static byte[] decode(char[] inChars)
	{
		//base64 编码的字符,最后的解码结果不会超出 字符总长度/4*3
		//因为有换行和最后的 Pad，如果都没有的情况下才是正好  字符总长度/4*3
		int capicity = inChars.length/4*3;
		byte[] buffer = new byte[capicity];
		int length = decode(inChars, buffer);
		byte[] result = new byte[length];
		for(int i=0;i<length;i++)
		{
			result[i] = buffer[i];
		}
		//byte[] result = Arrays.copyOfRange(buffer, 0, length);
		return result;
	}
	/**
	 * 将给定 base64 字符串中的数据解码为对应的 byte 数组。
	 * 返回解码后的数组长度。
	 * @param inChars
	 * @param outData
	 * @return
	 */
	public static int decode(char[] inChars, byte[] outData) {
		int charIndex = 0;
		int byteIndex = 0;
		while(true)
		{
			//如果已经到字符末尾，则跳出循环
			if(charIndex >= inChars.length)
				break;
			
			//如果遇到一个非 base64 字符，则获取下一个字符。
			//应该是换行, \r\n
			if(DECODE_TABLE[inChars[charIndex]] == -1)
			{
				charIndex++;
				continue;
			}
			
			//对正常的 4 个字符一组进行处理
			byte first = DECODE_TABLE[inChars[charIndex]];
			byte second = DECODE_TABLE[inChars[charIndex+1]];
			byte third = DECODE_TABLE[inChars[charIndex+2]];
			byte fourth = DECODE_TABLE[inChars[charIndex+3]];

			if(inChars[charIndex+2] == PAD)
			{
				//有两个 PAD，说明结果只有一个有效字节
				//获取第一个字节的 3-8 位作为新数据的前 1-6 位，用第二个字节的 3-4 位作为新字节的 7-8位
				//byte firstR = (byte)(((first << 2) & 0xfc) | ((second >> 4) & 0x03));
				byte firstR = (byte)((first << 2) | (second >> 4));
				outData[byteIndex++] = firstR;
			}
			else if(inChars[charIndex+3] == PAD)
			{
				//有一个 PAD，说明结果只有两个有效字节
				//获取第一个字节的 3-8 位作为新数据的前 1-6 位，用第二个字节的 3-4 位作为新字节的 7-8位
				//byte firstR = (byte)(((first << 2) & 0xfc) | ((second >> 4) & 0x03));
				byte firstR = (byte)((first << 2) | (second >> 4));
				//获取第二个字节的 5-8 位作为新数据的前 1-4 位，用第三个字节的 3-6 位作为新字节的 5-8位
				//byte secondR = (byte)(((second << 4) & 0xf0) | ((third >> 2) & 0x0f));
				byte secondR = (byte)((second << 4) | (third >> 2));
				outData[byteIndex++] = firstR;
				outData[byteIndex++] = secondR;
			}
			//都是有效字节
			else
			{
				//获取第一个字节的 3-8 位作为新数据的前 1-6 位，用第二个字节的 3-4 位作为新字节的 7-8位
				//byte firstR = (byte)(((first << 2) & 0xfc) | ((second >> 4) & 0x03));
				byte firstR = (byte)((first << 2) | (second >> 4));
				//获取第二个字节的 5-8 位作为新数据的前 1-4 位，用第三个字节的 3-6 位作为新字节的 5-8位
				//byte secondR = (byte)(((second << 4) & 0xf0) | ((third >> 2) & 0x0f));
				byte secondR = (byte)((second << 4) | (third >> 2));
				//获取第三个字节的 7-8 位作为新数据的前 1-2 位，用第三个字节的 3-8 位作为新字节的 3-8位
				//byte thirdR = (byte)(((third << 6) & 0xc0) | (fourth & 0xcf));
				byte thirdR = (byte)((third << 6) | fourth);
				outData[byteIndex++] = firstR;
				outData[byteIndex++] = secondR;
				outData[byteIndex++] = thirdR;
			}
			//最后的处理
			charIndex+=4;
			//对于包含附加字符的处理，附加字符为 =
		}
		//返回实际解码后的  byte 的字节数
		return byteIndex;
	}
	/**
	 * 将从指定位置开始的 base64 字符转换为对应的 byte
	 * @param toDecodeChar 要转换的 base64 字符数组
	 * @param charStart 从数组中的哪个位置开始转换
	 * @param destBytes 保存到 byte 数组中的位置
	 * @param byteStart 从 byte 数组中的哪个位置开始
	 */
	private static int CharsToBytes(char[] toDecodeChar, int charStart, byte[] destBytes, int byteStart)
	{
		//对 4 个字符进行处理
		byte first = DECODE_TABLE[toDecodeChar[charStart++]];
		byte second = DECODE_TABLE[toDecodeChar[charStart++]];
		byte third = DECODE_TABLE[toDecodeChar[charStart++]];
		byte fourth = DECODE_TABLE[toDecodeChar[charStart++]];
		
		//获取第一个字节的 3-8 位作为新数据的前 1-6 位，用第二个字节的 3-4 位作为新字节的 7-8位
		byte firstR = (byte)(((first << 2) & 0xfc) | ((second >> 4) & 0x03));
		//获取第二个字节的 5-8 位作为新数据的前 1-4 位，用第三个字节的 3-6 位作为新字节的 5-8位
		byte secondR = (byte)(((second << 4) & 0xf0) | ((third >> 2) & 0x0f));
		//获取第三个字节的 7-8 位作为新数据的前 1-2 位，用第三个字节的 3-8 位作为新字节的 3-8位
		byte thirdR = (byte)(((third << 6) & 0xc0) | (fourth & 0xcf));
		destBytes[byteStart++] = firstR;
		destBytes[byteStart++] = secondR;
		destBytes[byteStart++] = thirdR;
		return charStart;
	}
}
